#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <opus/opus.h>

// 音频参数
#define SAMPLE_RATE 16000    // 采样率 16kHz
#define CHANNELS 1           // 单声道
#define FRAME_SIZE 960       // 每帧样本数 (60ms at 16kHz)
#define MAX_FRAME_SIZE 6*960 // 最大帧大小
#define MAX_PACKET_SIZE 4000 // 最大包大小
#define DURATION_SECONDS 2   // 测试音频时长（秒）

// 生成正弦波信号
void generate_sine_wave(opus_int16 *pcm_data, int samples, float frequency, float amplitude) {
    for (int i = 0; i < samples; i++) {
        float t = (float)i / SAMPLE_RATE;
        float sample = amplitude * sin(2.0 * M_PI * frequency * t);
        pcm_data[i] = (opus_int16)(sample * 32767.0f); // 转换为16位整数
    }
}

// 保存PCM数据到文件
void save_pcm_to_file(const char* filename, opus_int16 *pcm_data, int samples) {
    FILE *file = fopen(filename, "wb");
    if (file) {
        fwrite(pcm_data, sizeof(opus_int16), samples, file);
        fclose(file);
        printf("PCM 数据已保存到: %s\n", filename);
    } else {
        printf("无法创建文件: %s\n", filename);
    }
}

// 计算音频的RMS（均方根）值
float calculate_rms(opus_int16 *pcm_data, int samples) {
    double sum = 0.0;
    for (int i = 0; i < samples; i++) {
        double sample = (double)pcm_data[i] / 32767.0;
        sum += sample * sample;
    }
    return sqrt(sum / samples);
}

// 比较两个PCM数组的相似度
float compare_pcm_arrays(opus_int16 *original, opus_int16 *decoded, int samples) {
    double mse = 0.0; // 均方误差
    for (int i = 0; i < samples; i++) {
        double diff = (double)(original[i] - decoded[i]) / 32767.0;
        mse += diff * diff;
    }
    mse /= samples;
    
    // 返回信噪比（SNR）的近似值
    double snr = -10.0 * log10(mse);
    return (float)snr;
}

int main() {
    printf("=== Opus 音频编解码测试 ===\n\n");
    
    // 计算总样本数
    int total_samples = SAMPLE_RATE * DURATION_SECONDS;
    int total_frames = total_samples / FRAME_SIZE;
    
    printf("测试参数:\n");
    printf("- 采样率: %d Hz\n", SAMPLE_RATE);
    printf("- 声道数: %d\n", CHANNELS);
    printf("- 每帧样本数: %d\n", FRAME_SIZE);
    printf("- 测试时长: %d 秒\n", DURATION_SECONDS);
    printf("- 总样本数: %d\n", total_samples);
    printf("- 总帧数: %d\n\n", total_frames);
    
    // 分配内存
    opus_int16 *original_pcm = malloc(total_samples * sizeof(opus_int16));
    opus_int16 *decoded_pcm = malloc(total_samples * sizeof(opus_int16));
    unsigned char *encoded_data = malloc(MAX_PACKET_SIZE);
    
    if (!original_pcm || !decoded_pcm || !encoded_data) {
        printf("内存分配失败!\n");
        return 1;
    }
    
    // 生成测试信号：440Hz + 880Hz 的混合正弦波
    printf("生成测试信号...\n");
    memset(original_pcm, 0, total_samples * sizeof(opus_int16));
    
    // 生成440Hz正弦波（A4音符）
    opus_int16 *temp_buffer = malloc(total_samples * sizeof(opus_int16));
    generate_sine_wave(temp_buffer, total_samples, 440.0f, 0.3f);
    for (int i = 0; i < total_samples; i++) {
        original_pcm[i] += temp_buffer[i];
    }
    
    // 添加880Hz正弦波（A5音符）
    generate_sine_wave(temp_buffer, total_samples, 880.0f, 0.2f);
    for (int i = 0; i < total_samples; i++) {
        original_pcm[i] += temp_buffer[i];
    }
    
    free(temp_buffer);
    
    // 计算原始信号的RMS
    float original_rms = calculate_rms(original_pcm, total_samples);
    printf("原始信号 RMS: %.4f\n", original_rms);
    
    // 保存原始PCM数据
    save_pcm_to_file("original_sine.pcm", original_pcm, total_samples);
    
    // 创建Opus编码器
    int error;
    OpusEncoder *encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, OPUS_APPLICATION_AUDIO, &error);
    if (error != OPUS_OK) {
        printf("创建Opus编码器失败: %s\n", opus_strerror(error));
        goto cleanup;
    }
    
    // 设置编码器参数
    opus_encoder_ctl(encoder, OPUS_SET_BITRATE(64000)); // 64 kbps
    opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(10)); // 最高复杂度
    
    // 创建Opus解码器
    OpusDecoder *decoder = opus_decoder_create(SAMPLE_RATE, CHANNELS, &error);
    if (error != OPUS_OK) {
        printf("创建Opus解码器失败: %s\n", opus_strerror(error));
        opus_encoder_destroy(encoder);
        goto cleanup;
    }
    
    printf("\n开始编解码过程...\n");
    
    // 逐帧编码和解码
    int decoded_samples_total = 0;
    int total_encoded_bytes = 0;
    
    for (int frame = 0; frame < total_frames; frame++) {
        int frame_start = frame * FRAME_SIZE;
        
        // 检查是否是最后一帧（可能不完整）
        int current_frame_size = FRAME_SIZE;
        if (frame_start + FRAME_SIZE > total_samples) {
            current_frame_size = total_samples - frame_start;
        }
        
        if (current_frame_size <= 0) break;
        
        // 编码
        int encoded_bytes = opus_encode(encoder, 
                                       &original_pcm[frame_start], 
                                       current_frame_size,
                                       encoded_data, 
                                       MAX_PACKET_SIZE);
        
        if (encoded_bytes < 0) {
            printf("编码失败 (帧 %d): %s\n", frame, opus_strerror(encoded_bytes));
            continue;
        }
        
        total_encoded_bytes += encoded_bytes;
        
        // 解码
        int decoded_samples = opus_decode(decoder,
                                         encoded_data,
                                         encoded_bytes,
                                         &decoded_pcm[decoded_samples_total],
                                         MAX_FRAME_SIZE,
                                         0);
        
        if (decoded_samples < 0) {
            printf("解码失败 (帧 %d): %s\n", frame, opus_strerror(decoded_samples));
            continue;
        }
        
        decoded_samples_total += decoded_samples;
        
        // 每100帧输出一次进度
        if ((frame + 1) % 100 == 0) {
            printf("已处理 %d/%d 帧\n", frame + 1, total_frames);
        }
    }
    
    printf("编解码完成!\n\n");
    
    // 计算统计信息
    printf("=== 编解码结果统计 ===\n");
    printf("原始样本数: %d\n", total_samples);
    printf("解码样本数: %d\n", decoded_samples_total);
    printf("总编码字节数: %d\n", total_encoded_bytes);
    
    // 计算压缩比
    int original_bytes = total_samples * sizeof(opus_int16);
    float compression_ratio = (float)original_bytes / total_encoded_bytes;
    printf("原始数据大小: %d 字节\n", original_bytes);
    printf("压缩比: %.2f:1\n", compression_ratio);
    
    // 计算平均比特率
    float avg_bitrate = (float)(total_encoded_bytes * 8) / DURATION_SECONDS / 1000.0f;
    printf("平均比特率: %.2f kbps\n", avg_bitrate);
    
    // 计算解码信号的RMS
    int compare_samples = (decoded_samples_total < total_samples) ? decoded_samples_total : total_samples;
    float decoded_rms = calculate_rms(decoded_pcm, compare_samples);
    printf("解码信号 RMS: %.4f\n", decoded_rms);
    
    // 计算信号质量（SNR）
    float snr = compare_pcm_arrays(original_pcm, decoded_pcm, compare_samples);
    printf("信噪比 (SNR): %.2f dB\n", snr);
    
    // 保存解码PCM数据
    save_pcm_to_file("decoded_sine.pcm", decoded_pcm, decoded_samples_total);
    
    printf("\n=== 质量评估 ===\n");
    if (snr > 30.0f) {
        printf("音质评级: 优秀 (SNR > 30 dB)\n");
    } else if (snr > 20.0f) {
        printf("音质评级: 良好 (SNR > 20 dB)\n");
    } else if (snr > 10.0f) {
        printf("音质评级: 一般 (SNR > 10 dB)\n");
    } else {
        printf("音质评级: 较差 (SNR < 10 dB)\n");
    }
    
    printf("\n提示: 可以使用以下命令播放PCM文件:\n");
    printf("ffplay -f s16le -ar %d -ac %d original_sine.pcm\n", SAMPLE_RATE, CHANNELS);
    printf("ffplay -f s16le -ar %d -ac %d decoded_sine.pcm\n", SAMPLE_RATE, CHANNELS);
    
    // 清理资源
    opus_encoder_destroy(encoder);
    opus_decoder_destroy(decoder);
    
cleanup:
    free(original_pcm);
    free(decoded_pcm);
    free(encoded_data);
    
    printf("\n测试完成!\n");
    return 0;
}
